﻿using System;
#if NETFX_CORE
using Windows.System;
using Key = Windows.System.VirtualKey;
using ModifierKeys = Windows.System.VirtualKeyModifiers;
#else
using System.Windows.Input;
#endif

namespace DaveSexton.Labs
{
	internal static class ConsoleKeyConverter
	{
		/// <summary>
		/// Converts the non-printable keys only (and Enter).
		/// </summary>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity",
			Justification = "Can't get much simpler.")]
		public static ConsoleKeyInfo? FromKey(Key key, ModifierKeys mods)
		{
#if NETFX_CORE
			bool shift = mods.HasFlag(VirtualKeyModifiers.Shift);
			bool alt = false;
			bool control = mods.HasFlag(VirtualKeyModifiers.Control);
#elif !WINDOWS_PHONE
			bool shift = mods.HasFlag(ModifierKeys.Shift);
			bool alt = mods.HasFlag(ModifierKeys.Alt);
			bool control = mods.HasFlag(ModifierKeys.Control);
#else
			bool shift = (mods & ModifierKeys.Shift) == ModifierKeys.Shift;
			bool alt = (mods & ModifierKeys.Alt) == ModifierKeys.Alt;
			bool control = (mods & ModifierKeys.Control) == ModifierKeys.Control;
#endif

			switch (key)
			{
				case Key.Space:
					// In testing, only the WPF KeyUp event is raised for space, not the TextInput event
					return new ConsoleKeyInfo(' ', ConsoleKey.Spacebar, shift, alt, control);
				case Key.Back:
					return new ConsoleKeyInfo('\b', ConsoleKey.Backspace, shift, alt, control);
				case Key.Delete:
					return new ConsoleKeyInfo('\x007f', ConsoleKey.Delete, shift, alt, control);
				case Key.Down:
					return new ConsoleKeyInfo('↓', ConsoleKey.DownArrow, shift, alt, control);
				case Key.End:
					return new ConsoleKeyInfo('ꜜ', ConsoleKey.End, shift, alt, control);
				case Key.Enter:
					return new ConsoleKeyInfo('\r', ConsoleKey.Enter, shift, alt, control);
				case Key.Escape:
					return new ConsoleKeyInfo('\x001b', ConsoleKey.Escape, shift, alt, control);
				case Key.F1:
					return new ConsoleKeyInfo('1', ConsoleKey.F1, shift, alt, control);
				case Key.F2:
					return new ConsoleKeyInfo('2', ConsoleKey.F2, shift, alt, control);
				case Key.F3:
					return new ConsoleKeyInfo('3', ConsoleKey.F3, shift, alt, control);
				case Key.F4:
					return new ConsoleKeyInfo('4', ConsoleKey.F4, shift, alt, control);
				case Key.F5:
					return new ConsoleKeyInfo('5', ConsoleKey.F5, shift, alt, control);
				case Key.F6:
					return new ConsoleKeyInfo('6', ConsoleKey.F6, shift, alt, control);
				case Key.F7:
					return new ConsoleKeyInfo('7', ConsoleKey.F7, shift, alt, control);
				case Key.F8:
					return new ConsoleKeyInfo('8', ConsoleKey.F8, shift, alt, control);
				case Key.F9:
					return new ConsoleKeyInfo('9', ConsoleKey.F9, shift, alt, control);
				case Key.F10:
					return new ConsoleKeyInfo('0', ConsoleKey.F10, shift, alt, control);
				case Key.F11:
					return new ConsoleKeyInfo('1', ConsoleKey.F11, shift, alt, control);
				case Key.F12:
					return new ConsoleKeyInfo('2', ConsoleKey.F12, shift, alt, control);
				case Key.Home:
					return new ConsoleKeyInfo('ꜛ', ConsoleKey.Home, shift, alt, control);
				case Key.Insert:
					return new ConsoleKeyInfo('►', ConsoleKey.Insert, shift, alt, control);
				case Key.Left:
					return new ConsoleKeyInfo('←', ConsoleKey.LeftArrow, shift, alt, control);
				case Key.PageDown:
					return new ConsoleKeyInfo('┬', ConsoleKey.PageDown, shift, alt, control);
				case Key.PageUp:
					return new ConsoleKeyInfo('┴', ConsoleKey.PageUp, shift, alt, control);
				case Key.Right:
					return new ConsoleKeyInfo('→', ConsoleKey.RightArrow, shift, alt, control);
				case Key.Up:
					return new ConsoleKeyInfo('↑', ConsoleKey.UpArrow, shift, alt, control);
				default:
					return null;
			}
		}

		public static ConsoleKeyInfo FromChar(char key, ModifierKeys mods)
		{
#if NETFX_CORE
			bool shift = mods.HasFlag(VirtualKeyModifiers.Shift);
			bool alt = false;
			bool control = mods.HasFlag(VirtualKeyModifiers.Control);
#elif !WINDOWS_PHONE
			bool shift = mods.HasFlag(ModifierKeys.Shift);
			bool alt = mods.HasFlag(ModifierKeys.Alt);
			bool control = mods.HasFlag(ModifierKeys.Control);
#else
			bool shift = (mods & ModifierKeys.Shift) == ModifierKeys.Shift;
			bool alt = (mods & ModifierKeys.Alt) == ModifierKeys.Alt;
			bool control = (mods & ModifierKeys.Control) == ModifierKeys.Control;
#endif

			if (char.IsLetterOrDigit(key))
			{
				return new ConsoleKeyInfo(key, (ConsoleKey) char.ToUpperInvariant(key), shift, alt, control);
			}

			switch (key)
			{
				case '\r':
					return new ConsoleKeyInfo('\r', ConsoleKey.Enter, shift, alt, control);
				case (char) 27:
					return new ConsoleKeyInfo(key, ConsoleKey.Escape, shift, alt, control);
				case '+':
					return new ConsoleKeyInfo(key, ConsoleKey.Add, shift, alt, control);
				case '.':
					return new ConsoleKeyInfo(key, ConsoleKey.Decimal, shift, alt, control);
				case '/':
					return new ConsoleKeyInfo(key, ConsoleKey.Divide, shift, alt, control);
				case '*':
					return new ConsoleKeyInfo(key, ConsoleKey.Multiply, shift, alt, control);
				case ' ':
					return new ConsoleKeyInfo(key, ConsoleKey.Spacebar, shift, alt, control);
				case '-':
					return new ConsoleKeyInfo(key, ConsoleKey.Subtract, shift, alt, control);
				case '\t':
					return new ConsoleKeyInfo('\t', ConsoleKey.Tab, shift, alt, control);
				default:
					return new ConsoleKeyInfo(key, ConsoleKey.NoName, shift, alt, control);
			}
		}
	}
}